using System;
using System.Collections;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using NetduinoHappyHour.Util;
using SecretLabs.NETMF.Hardware.Netduino;

namespace NetduinoHappyHour.Hardware
{
    /// <summary>
    /// Used to keep all configuaration in one place and do some system documentation
    /// Analog pin usage
    /// GPIO_PIN_A0 used by Explore
    /// GPIO_PIN_A1 used by Explore
    /// GPIO_PIN_A2
    /// GPIO_PIN_A3 used by CheckBattery
    /// GPIO_PIN_A4 DisplayMessage, I2cLcdTransferProvider (I2C Bus)
    /// GPIO_PIN_A5 DisplayMessage, I2cLcdTransferProvider (I2C Bus)
    /// Digital pin usage
    /// GPIO_PIN_D0 used by CommandReadWrite/Interactive/SystemConfig.ComPortName for SerialPort COM1
    /// GPIO_PIN_D1 used by CommandReadWrite/Interactive/SystemConfig.ComPortName for SerialPort COM1
    /// GPIO_PIN_D2 used by JohnWilson Trigger
    /// GPIO_PIN_D3 used by JohnWilson Echo
    /// GPIO_PIN_D4 used by DisplayMessage, GpioLcdTransferProvider
    /// GPIO_PIN_D5 used by DisplayMessage, GpioLcdTransferProvider !!!!!
    /// GPIO_PIN_D5 used by DriveCommandDC, pwm port                !!!!!
    /// GPIO_PIN_D6 used by DisplayMessage, GpioLcdTransferProvider !!!!!
    /// GPIO_PIN_D6 used by DriveCommandDC, pwm port                !!!!!
    /// GPIO_PIN_D7 used by DisplayMessage, GpioLcdTransferProvider
    /// GPIO_PIN_D8 used by DisplayMessage, GpioLcdTransferProvider
    /// GPIO_PIN_D9 used by DisplayMessage, GpioLcdTransferProvider !!!!!
    /// GPIO_PIN_D9 used by StepperSleep to Execute EngineSleepMode !!!!!
    /// 
    /// Written by: Salvador Richter
    /// Appache License Version 2.0 
    /// </summary>
    public class SystemConfig
    {
        public enum Display
        {
            None,
            I2C,
            Parallel,
            All
        };

        private static SystemConfig _instance = null;

        /// <summary>
        /// Table to store used pins including whom is using the pin, used by RegisterPin(..,..)
        /// </summary>
        private Hashtable _pinTable = new Hashtable();

        /// <summary>
        /// used by DriveCommand/DriveCommandLeft (clock left)
        /// </summary>
        public readonly Cpu.Pin StepperClockLeft = Pins.GPIO_PIN_D11;
        
        /// <summary>
        /// used by DriveCommand/DriveCommandLeft (direction left)
        /// </summary>
        public readonly Cpu.Pin StepperDirectionLeft = Pins.GPIO_PIN_D10;

        /// <summary>
        /// used by DriveCommand/DriveCommandRight (clock right)
        /// </summary>
        public readonly Cpu.Pin StepperClockRight = Pins.GPIO_PIN_D13;
        
        /// <summary>
        /// used by DriveCommand/DriveCommandRight (direction right)
        /// </summary>
        public readonly Cpu.Pin StepperDirectionRight = Pins.GPIO_PIN_D12;

        /// <summary>
        /// Minimum delay between two steps
        /// </summary>
        public int MinDelayStepper { get; set; }

        /// <summary>
        /// usefull delay between two steps
        /// </summary>
        public int DelayStepper { get; set; }
        
        /// <summary>
        /// Number of steps one wheel forward or backward other wheel stays still to turn 90 degrees
        /// </summary>
        public int Turn90 { get; set; }
        
        /// <summary>
        /// Number of steps one wheel forward other backward to rotate 90 degrees
        /// </summary>
        public int Rotate90 { get; set; }

        /// <summary>
        /// Number of steps per wheel rotation
        /// </summary>        
        public int WheelRotation { get; set; }

        /// <summary>
        /// used by DriveCommandDC (pwm signal left)
        /// </summary>
        public readonly Cpu.Pin DCMotorPwmLeft = Pins.GPIO_PIN_D6;

        /// <summary>
        /// used by DriveCommandDC (pwm signal right)
        /// </summary>
        public readonly Cpu.Pin DCMotorPwmRight = Pins.GPIO_PIN_D5;

        /// <summary>
        /// Determines if AdaFruit Shield is to be used for DC engine driving
        /// </summary>
        public bool UseAdaFruitForDcMotorDriving { get; set; }

        /// <summary>
        /// Determines if Stepper engines or DC engines shall be used for driving
        /// </summary>
        public bool UseStepperDriving { get; set; }

        /// <summary>
        /// Which type of LCD display to be used, true i2c display, false parallel IO display
        /// </summary>
        public Display DisplayType { get; set; }

        /// <summary>
        /// Which type of LCD display to be used, true i2c display, false parallel IO display
        /// </summary>
        public string ComPortName { get; set; }

        /// <summary>
        /// Which type of LCD display to be used, true i2c display, false parallel IO display
        /// </summary>
        public int ComPortSpeed { get; set; }

        // Wheel distance: 194 mm
        // Wheel diameter 90 mm
        // Wheel circumference 282,7 mm

        /// <summary>
        /// private constructor to avoid instantiation. 
        /// inside constructor all properties are set with default values
        /// </summary>
        private SystemConfig() 
        {
            // delays from 1 to 10 ms give appropriate speed on prototype transmittion ratio
            MinDelayStepper = 1;
            DelayStepper = 10;
            // TODO calc proper values
            Turn90 = 1218;
            Rotate90 = 112;
            WheelRotation = 200;
            UseAdaFruitForDcMotorDriving = true;
            UseStepperDriving = true;
            DisplayType = Display.I2C;
            ComPortName = "COM1";
            // btm 222: 115200
            // hc06: 9600
            ComPortSpeed = 115200;
        }

        public static SystemConfig Instance()
        {
            if (_instance == null)
            {
                _instance = new SystemConfig();
            }
            return _instance;
        }

        /// <summary>
        /// Registrates the usage of a IO Pin
        /// </summary>
        /// <param name="pin">The pin to registrate</param>
        /// <param name="registrator">Who is registrating the pin</param>
        public void RegisterPin(Cpu.Pin pin, String registrator)
        {
            if (!_pinTable.Contains(pin))
            {
                _pinTable.Add(pin, registrator);
            }
            else
            {
                throw new System.InvalidOperationException(registrator + " tried to register pin " + pin.ToString() + " already in use by " + _pinTable[pin] + " !");
            }
        }

        /// <summary>
        /// Deregistrates the usage of a IO Pin
        /// </summary>
        /// <param name="pin">The pin to deregistrate</param>
        public void DeregisterPin(Cpu.Pin pin)
        {
            _pinTable.Remove(pin);
        }

        /// <summary>
        /// Checks the regustration status of a IO Pin
        /// </summary>
        /// <param name="pin">The pin to check</param>
        public bool IsPinRegistered(Cpu.Pin pin)
        {
            return _pinTable.Contains(pin);
        }
    }
}
